#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <asm/uaccess.h>
#include "common.h"

#define DEV_CNT			    1
#define DEV_NAME			"demo"

#define BUF_LEN 256

/* demo设备结构体 */
struct demo_dev{
	dev_t devid;			// 设备号
	struct cdev cdev;		// cdev
	struct class *class;	// 类
	struct device *device;	// 设备
	int major;				// 主设备号
	int minor;				// 次设备号

    u8 buf[BUF_LEN];
};

struct demo_dev *demo;

static int demo_open(struct inode *inode, struct file *filp)
{
    struct cdev *cdev = inode->i_cdev;
	struct demo_dev *dev = container_of(cdev, struct demo_dev, cdev);
    XXD("IN");
	filp->private_data = dev;

    XXD("OUT");
	return 0;
}

static ssize_t demo_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    XXD("IN"); 
    XXD("OUT");
	return 0;
}

static ssize_t demo_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    XXD("IN"); 
    XXD("OUT");
	return cnt;
}

static int demo_release(struct inode *inode, struct file *filp)
{
    XXD("IN");
    XXD("OUT");
	return 0;
}

static const struct file_operations demo_fops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.read = demo_read,
	.write = demo_write,
	.release = 	demo_release,
};

static int __init demo_init(void)
{
    int ret;
    XXD("IN");

    demo = (struct demo_dev *)kvzalloc(sizeof(struct demo_dev), GFP_KERNEL);
    if (!demo) {
        XXE("Not enough memory demo_dev raw device structures");
        return -ENOMEM;
	}

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (demo->major) {		/*  定义了设备号 */
		demo->devid = MKDEV(demo->major, 0);
		ret = register_chrdev_region(demo->devid, DEV_CNT, DEV_NAME);
        if (ret) {
            XXE("register_chrdev_region() failed for demo");
		    goto error_region;
        }
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&demo->devid, 0, DEV_CNT, DEV_NAME);	/* 申请设备号 */
        if (ret < 0) {
            XXE("alloc_chrdev_region() failed for demo");
            goto error_region;
        }
		demo->major = MAJOR(demo->devid);	/* 获取分配号的主设备号 */
		demo->minor = MINOR(demo->devid);	/* 获取分配号的次设备号 */
	}

	/* 2、初始化cdev */
	demo->cdev.owner = THIS_MODULE;
	cdev_init(&demo->cdev, &demo_fops);

	/* 3、添加一个cdev */
	ret = cdev_add(&demo->cdev, demo->devid, DEV_CNT);
    if (ret){
        XXE("cdev_add() failed for demo");
		goto error_cdev;
    }

	/* 4、创建类 */
	demo->class = class_create(THIS_MODULE, DEV_NAME);
	if (IS_ERR(demo->class)) {
        XXE("class_create() failed for demo");
		ret = PTR_ERR(demo->class);
        goto error_class;
	}

	/* 5、创建设备 */
	demo->device = device_create(demo->class, NULL, demo->devid, NULL, DEV_NAME);
	if (IS_ERR(demo->device)) {
        XXE("device_create() failed for demo");
		ret = PTR_ERR(demo->device);
        goto error_device;
	}

    XXD("OUT");
	return 0;
error_device:
    class_destroy(demo->class);
error_class:
    cdev_del(&demo->cdev);// 删除cdev
error_cdev:
    unregister_chrdev_region(demo->devid, DEV_CNT); //注销设备号
error_region:
    kvfree(demo);

	return ret;
}

static void __exit demo_exit(void)
{
    XXD("IN");
	device_destroy(demo->class, demo->devid);
	class_destroy(demo->class);
	cdev_del(&demo->cdev); //删除cdev
	unregister_chrdev_region(demo->devid, DEV_CNT); //注销设备号

    kvfree(demo);
    XXD("OUT");
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("landaliming");